package com.vainycos.lotterysearch.config;

import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.vainycos.lotterysearch.mapper.WorldCupPredictMapper;
import com.vainycos.lotterysearch.model.WorldCupDetailDO;
import com.vainycos.lotterysearch.model.WorldCupPredictDO;
import com.vainycos.lotterysearch.model.worldcup.Worldcup;
import com.vainycos.lotterysearch.service.WorldCupDetailService;
import com.vainycos.lotterysearch.service.notify.DingNotify;
import com.vainycos.lotterysearch.utils.DateUtil;
import com.vainycos.lotterysearch.utils.FreeApiUtil;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.config.CronTask;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.PreDestroy;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;

@Slf4j
@Configuration
public class ScheduledTaskCenter implements SchedulingConfigurer {
    private volatile ScheduledTaskRegistrar registrar;

    private final ConcurrentHashMap<String, ScheduledFuture<?>> scheduledFuture = new ConcurrentHashMap<>();

    private final ConcurrentHashMap<String, CronTask> cronTasks = new ConcurrentHashMap<>();

    @Autowired
    private DingNotify dingNotify;

    @Autowired
    private WorldCupDetailService worldCupDetailService;

    @Autowired
    private WorldCupPredictMapper worldCupPredictMapper;

    @Override
    public void configureTasks(ScheduledTaskRegistrar registrar) {
        ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(30,
                new BasicThreadFactory.Builder().namingPattern("msg-schedule-pool-%d").daemon(true).build());
        registrar.setScheduler(executorService);
        this.registrar = registrar;
    }

    /**
     * 初始化任务中心
     * @param worldCupDetailDOS 集合
     */
    @Async
    public void initTaskCenter(List<WorldCupDetailDO> worldCupDetailDOS){

        // 添加定时任务
        worldCupDetailDOS.forEach(e->{
            String taskId = e.getMatchName() + e.getStartTime() + e.getLeftName() + e.getRightName() + "init";
            // 提前15分钟发送通知
            String expression = DateUtil.convertExpression(DateUtil.dateAddMinutes(e.getStartTime(), -15));
            addSingleTaskMsgConfig(taskId, expression, e);
            log.info("初始化定时任务->{}，{}",e.getMatchName(), expression);
        });
    }


    /**
     * [消息任务配置]: 提交任务
     */
    public void addSingleTaskMsgConfig(String taskId, String expression, WorldCupDetailDO worldCupDetailDO){
        if(scheduledFuture.containsKey(taskId) && cronTasks.get(taskId).getExpression().equals(expression)){
            log.info("[定时任务中心]计划任务已存在并且表达式未发生变化");
            return;
        }
        // 如果策略执行时间发生了变化
        if(scheduledFuture.containsKey(taskId)){
            log.info("[定时任务中心]策略执行时间发生了变化");
            // 移除后重新添加
            scheduledFuture.get(taskId).cancel(false);
            scheduledFuture.remove(taskId);
            cronTasks.remove(taskId);
        }
        // 新任务
        CronTask task = new CronTask(new Runnable() {
            @Override
            public void run() {
                // 发送钉钉消息
                dingNotify.notifyMarkdown("比赛开赛提醒", matchStartNotify(worldCupDetailDO));
                log.info("[定时任务中心]每日定时任务执行成功->{}", expression);
            }
        }, expression);
        ScheduledFuture<?> future = registrar.getScheduler().schedule(task.getRunnable(), task.getTrigger());
        cronTasks.put(taskId, task);
        scheduledFuture.put(taskId, future);
        log.info("[定时任务中心][消息任务配置]定时任务注册成功,执行策略->{}", expression);
    }

    /**
     * 实况比赛任务
     * @param taskId
     * @param expression
     * @param worldCupDetailDO
     */
    public void addRealtimeMatchTaskMsgConfig(String taskId, String expression, WorldCupDetailDO worldCupDetailDO){
        if(scheduledFuture.containsKey(taskId) && cronTasks.get(taskId).getExpression().equals(expression)){
            log.info("[定时任务中心]计划任务已存在并且表达式未发生变化");
            return;
        }
        // 如果策略执行时间发生了变化
        if(scheduledFuture.containsKey(taskId)){
            log.info("[定时任务中心]策略执行时间发生了变化");
            // 移除后重新添加
            scheduledFuture.get(taskId).cancel(false);
            scheduledFuture.remove(taskId);
            cronTasks.remove(taskId);
        }
        // 新任务
        CronTask task = new CronTask(new Runnable() {
            @Override
            public void run() {
                // 发送钉钉消息
                String s = realtimeMatch(worldCupDetailDO).getString("content");
                if (s == null || s.equals("比分无变化")){
                    log.info("[定时任务中心]比分暂无变化->{}", worldCupDetailDO.getLeftName() + "-" + worldCupDetailDO.getRightName());
                }else {
                    dingNotify.notifyMarkdown("实况比赛", s);
                    log.info("[定时任务中心]比分变化->{}, {}", worldCupDetailDO.getLeftName() + "-" + worldCupDetailDO.getRightName(), worldCupDetailDO.getLeftScore() + ":" + worldCupDetailDO.getRightScore());
                }

            }
        }, expression);
        ScheduledFuture<?> future = registrar.getScheduler().schedule(task.getRunnable(), task.getTrigger());
        cronTasks.put(taskId, task);
        scheduledFuture.put(taskId, future);
        log.info("[定时任务中心][消息任务配置]定时任务注册成功,执行策略->{}", expression);
    }

    /**
     * 根据taskId移除定时任务
     * @param taskId
     */
    public void removeTaskByTaskId(String taskId){
        scheduledFuture.get(taskId).cancel(false);
        scheduledFuture.remove(taskId);
        cronTasks.remove(taskId);
    }

    /**
     * 赛前15分钟预告
     * @param e
     * @return
     */
    private String matchStartNotify(WorldCupDetailDO e){
        StringBuffer stringBuffer = new StringBuffer();
        int rank = worldCupDetailService.rank(e.getStartTime());
        stringBuffer.append("# 距离比赛开始还有15分钟" + " \n");
        stringBuffer.append(e.getMatchName() + " " + e.getStartTime() + " \n");
        stringBuffer.append(e.getLeftName() + "-" + e.getRightName() + " ");
        if (e.getLeftName().contains("阿根廷") || e.getRightName().contains("阿根廷")){
            stringBuffer.append("为梅西打call，all in 阿根廷，阿根廷冲啊!~" + " \n");
        }
        stringBuffer.append("关注热度：" + e.getHot() + " 热度排名第" + rank + " \n");
        stringBuffer.append(rank < 11 ? "非常值得一看！~ \n" : "");
        stringBuffer.append("享受比赛吧");
        return stringBuffer.toString();
    }

    /**
     * 实时比赛实况
     * @param e
     * @return
     */
    private JSONObject realtimeMatch(WorldCupDetailDO e){
        JSONObject result = new JSONObject();
        Worldcup worldcupByDate = FreeApiUtil.getWorldcupByDate(e.getTime().substring(5));
        worldcupByDate.getData().get(0).getList().forEach(match->{
            // 转换
            WorldCupDetailDO waitForUpdate = new WorldCupDetailDO();
            waitForUpdate.setId(e.getId());
            waitForUpdate.setTime(match.getTime());
            waitForUpdate.setDate(match.getDate());
            waitForUpdate.setStartTime(match.getStartTime());
            waitForUpdate.setMatchName(match.getMatchName());
            waitForUpdate.setStatus(match.getStatus());
            waitForUpdate.setStatusText(match.getStatusText());
            waitForUpdate.setHot(match.getHot());
            waitForUpdate.setLeftName(match.getLeftLogo().getName());
            waitForUpdate.setLeftLogo(match.getLeftLogo().getLogo());
            waitForUpdate.setLeftScore(match.getLeftLogo().getScore());
            waitForUpdate.setRightName(match.getRightLogo().getName());
            waitForUpdate.setRightLogo(match.getRightLogo().getLogo());
            waitForUpdate.setRightScore(match.getRightLogo().getScore());
            waitForUpdate.setContent(JSONObject.toJSONString(match));
            // 更新数据库
            worldCupDetailService.updateById(waitForUpdate);
            String s = waitForUpdate.getMatchName() + waitForUpdate.getStartTime() + waitForUpdate.getLeftName() + waitForUpdate.getRightName();
            String s1 = e.getMatchName() + e.getStartTime() + e.getLeftName() + e.getRightName();
            if (s.equals(s1)){
                // 判断是否结束
                if ("3".equals(match.getStatus())){
                    removeTaskByTaskId(s1 + "realtime");
                    // predict预测表更新
                    WorldCupPredictDO worldCupPredictDO = updatePredict(waitForUpdate);
                    String content = "[实况赛事]"
                            + waitForUpdate.getLeftName()
                            + "-" + waitForUpdate.getRightName()
                            + " 比赛结束 终场比分 "
                            + waitForUpdate.getLeftScore() + ":" + waitForUpdate.getRightScore()
                            + " 预测获胜队伍为" + worldCupPredictDO.getPredictResult()
                            + ", 预测结果为" + worldCupPredictDO.getPredictResult()
                            ;
                    result.put("content", content);
                }else {
                    // 判断比分是否相同 只要有一个比分不同就判断不同
                    boolean left = e.getLeftScore().equals(waitForUpdate.getLeftScore());
                    boolean right = e.getRightScore().equals(waitForUpdate.getRightScore());
                    if (!left){
                        // 查询最新信息并更新提供比分 有比分变动才进行更新否则不输出
                        waitForUpdate.getLeftName();
                        result.put("content", "[实况赛事]主队进球了！" + waitForUpdate.getLeftName() + "-" + waitForUpdate.getRightName() + " " + waitForUpdate.getLeftScore() + ":" + waitForUpdate.getRightScore());
                    }else if (!right){
                        waitForUpdate.getRightName();
                        result.put("content", "[实况赛事]客队进球了！" + waitForUpdate.getLeftName() + "-" + waitForUpdate.getRightName() + " " + waitForUpdate.getLeftScore() + ":" + waitForUpdate.getRightScore());
                    }else {
                        // 比分无变化
                        result.put("content", "比分无变化");
                    }
                }

            }
        });
        return result;
    }

    /**
     * 预测表更新
     * @param worldCupDetailDO
     */
    private WorldCupPredictDO updatePredict(WorldCupDetailDO worldCupDetailDO){
        WorldCupPredictDO worldCupPredictDO = worldCupPredictMapper.selectOne(new LambdaQueryWrapper<WorldCupPredictDO>()
                .eq(WorldCupPredictDO::getStartTime, worldCupDetailDO.getStartTime())
                .eq(WorldCupPredictDO::getLeftName, worldCupDetailDO.getLeftName())
                .eq(WorldCupPredictDO::getRightName, worldCupDetailDO.getRightName())
                .last("limit 1")
        );
        if (worldCupPredictDO != null){
            String leftScore = worldCupPredictDO.getLeftScore();
            String rightScore = worldCupPredictDO.getRightScore();
            worldCupPredictDO.setLeftScore(leftScore);
            worldCupPredictDO.setRightScore(rightScore);
            // 主队获胜
            if (Integer.valueOf(leftScore) > Integer.valueOf(rightScore)){
                worldCupPredictDO.setRealVictory(worldCupPredictDO.getLeftName());
            }else if(Integer.valueOf(leftScore) == Integer.valueOf(rightScore)){
                // 平局
                worldCupPredictDO.setRealVictory("平局");
            }else {
                // 客队获胜
                worldCupPredictDO.setRealVictory(worldCupPredictDO.getRightName());
            }
            worldCupPredictDO.setPredictResult(worldCupPredictDO.getRealVictory().equals(worldCupPredictDO.getPredictVictory()) ? "成功" : "失败");
        }
        worldCupPredictMapper.updateById(worldCupPredictDO);
        return worldCupPredictDO;
    }

    /**
     * [消息任务配置]: 摧毁任务中心
     */
    @PreDestroy
    void destroy(){
        this.registrar.destroy();
        log.info("任务中心已被摧毁!");
    }

}

